using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using GeneticAlgorithms.Alphabets;

namespace GeneticAlgorithms.Genomes
{
    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="TGene">The type of the gene.</typeparam>
    public class ArrayGenome<TGene> : IGenome<TGene>, ICloneable
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="ArrayGenome&lt;TGene&gt;"/> class.
        /// </summary>
        /// <param name="alphabet">The alphabet.</param>
        /// <param name="length">The length.</param>
        public ArrayGenome(IAlphabet<TGene> alphabet, int length)
        {
            genes = new TGene[length];
            this.alphabet = alphabet;
        }

        #endregion
        
        #region Properties
        
        private TGene[] genes;

        #endregion

        #region IGenome Members

        /// <summary>
        /// Gets or sets the gene at the specified index.
        /// </summary>
        /// <value>
        /// The gene at the specified index.
        /// </value>
        public TGene this[int index]
        {
            get { return genes[index]; }
            set { genes[index] = value; }
        }

        /// <summary>
        /// Gets or sets the genome's fitness.
        /// </summary>
        /// <value>The fitness.</value>
        public double? Fitness
        {
            get;
            set;
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
        /// </returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return genes.GetEnumerator();
        }

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="System.Collections.Generic.IEnumerator{TGene}"/> that can be used to iterate through the collection.
        /// </returns>
        public IEnumerator<TGene> GetEnumerator()
        {
            foreach (TGene gene in genes)
            {
                yield return gene;
            }
        }

        /// <summary>
        /// Gets the genome's length (total number of genes in it).
        /// </summary>
        /// <value>The length.</value>
        public int Length
        {
            get { return genes.Length; }
        }

        private IAlphabet<TGene> alphabet;
        /// <summary>
        /// Gets the genome alphabet.
        /// </summary>
        /// <value>The genome alphabet.</value>
        /// <remarks>
        /// A genetic alphabet describes all values the genes may have.
        /// </remarks>
        public IAlphabet<TGene> Alphabet
        {
            get 
            { 
                return alphabet; 
            }
        }

        #endregion

        #region ICloneable Members

        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        public object Clone()
        {
            return new ArrayGenome<TGene>(Alphabet, Length);
        }

        #endregion

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder("[");

            foreach (TGene gene in genes)
            {
                sb.Append(gene.ToString());
                sb.Append(", ");
            }

            sb.Remove(sb.Length - 2, 2);
            sb.Append("]");

            /*
            if (fitness.HasValue)
                sb.AppendFormat(" ({0})", fitness.Value);
            */
            return sb.ToString();
        }
    }
}
